//// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
//package com.linqingying.gitcode.pullrequest.data
//
//import com.intellij.collaboration.async.classAsCoroutineName
//import com.intellij.collaboration.ui.html.AsyncHtmlImageLoader
//import com.intellij.collaboration.ui.icon.AsyncImageIconsProvider
//import com.intellij.collaboration.ui.icon.CachingIconsProvider
//import com.intellij.openapi.components.Service
//import com.intellij.openapi.components.service
//import com.intellij.openapi.diagnostic.logger
//import com.intellij.openapi.progress.*
//import com.intellij.openapi.project.Project
//import com.intellij.platform.util.coroutines.childScope
//import com.intellij.util.IconUtil
//import com.intellij.util.ui.ImageUtil
//import git4idea.remote.GitRemoteUrlCoordinates
//import icons.CollaborationToolsIcons
//import kotlinx.coroutines.*
//import kotlinx.coroutines.future.asDeferred
//import kotlinx.coroutines.sync.Mutex
//import kotlinx.coroutines.sync.withLock
//import com.linqingying.gitcode.api.*
//import com.linqingying.gitcode.api.data.GCUser
//import com.linqingying.gitcode.api.util.SimpleGCGQLPagesLoader
//import com.linqingying.gitcode.authentication.accounts.GitCodeAccount
//import com.linqingying.gitcode.authentication.accounts.GitCodeAccountInformationProvider
//import com.linqingying.gitcode.pullrequest.GCPRDiffRequestModelImpl
//import com.linqingying.gitcode.pullrequest.data.service.*
//import com.linqingying.gitcode.util.CachingGCUserAvatarLoader
//import com.linqingying.gitcode.util.GitCodeSharedProjectSettings
//import java.awt.Image
//import java.awt.Toolkit
//import java.io.IOException
//import java.util.concurrent.ConcurrentHashMap
//import javax.swing.Icon
//
//@Service(Service.Level.PROJECT)
//internal class GCPRDataContextRepository(private val project: Project, parentCs: CoroutineScope) {
//
//  private val cs = parentCs.childScope()
//
//  private val cache = ConcurrentHashMap<GCRepositoryCoordinates, GCPRDataContext>()
//  private val cacheGuard = Mutex()
//
//  suspend fun getContext(repository: GCRepositoryCoordinates, remote: GitRemoteUrlCoordinates,
//                         account: GitCodeAccount, requestExecutor: GitCodeApiRequestExecutor): GCPRDataContext =
//    withContext(cs.coroutineContext) {
//      cacheGuard.withLock {
//        val existing = cache[repository]
//        if (existing != null) return@withLock existing
//        try {
//          val contextScope = cs.childScope(classAsCoroutineName<GCPRDataContext>())
//          val context = contextScope.getContextAsync(account, requestExecutor, repository, remote).await()
//          cache[repository] = context
//          context
//        }
//        catch (e: Exception) {
//          if (e !is CancellationException) LOG.info("Error occurred while creating data context", e)
//          throw e
//        }
//      }
//    }
//
//  suspend fun clearContext(repository: GCRepositoryCoordinates) {
//    withContext(cs.coroutineContext) {
//      cacheGuard.withLock {
//        cache.remove(repository)?.scope?.coroutineContext?.get(Job)?.cancelAndJoin()
//      }
//    }
//  }
//
//  @Throws(IOException::class)
//  private fun CoroutineScope.getContextAsync(account: GitCodeAccount,
//                                             requestExecutor: GitCodeApiRequestExecutor,
//                                             parsedRepositoryCoordinates: GCRepositoryCoordinates,
//                                             remoteCoordinates: GitRemoteUrlCoordinates)
//    : Deferred<GCPRDataContext> {
//    val cs = this
//    return async {
//      val accountDetails = withContext(Dispatchers.IO) {
//        coroutineToIndicator {
//          val indicator = ProgressManager.getInstance().progressIndicator ?: EmptyProgressIndicator()
//          GitCodeAccountInformationProvider.getInstance().getInformation(requestExecutor, indicator, account)
//        }
//      }
//
//      val ghostUserDetails = requestExecutor.executeSuspend(GCGQLRequests.User.find(account.server, "ghost"))
//                             ?: error("Couldn't load ghost user details")
//
//      val repositoryInfo =
//        requestExecutor.executeSuspend(
//          GCGQLRequests.Repo.find(GCRepositoryCoordinates(account.server, parsedRepositoryCoordinates.repositoryPath))
//        )
//        ?: throw IllegalArgumentException(
//          "Repository ${parsedRepositoryCoordinates.repositoryPath} does not exist at ${account.server} or you don't have access.")
//
//      val currentUser = GCUser(accountDetails.nodeId, accountDetails.login, accountDetails.htmlUrl, accountDetails.avatarUrl!!,
//                               accountDetails.name)
//
//      // Image loaders
//      val iconsScope = cs.childScope(Dispatchers.Main)
//      val imageLoader = AsyncHtmlImageLoader { _, src ->
//        withContext(cs.coroutineContext + IMAGES_DISPATCHER) {
//          coroutineToIndicator {
//            val bytes = requestExecutor.execute(ProgressManager.getInstance().progressIndicator, GitCodeApiRequests.getBytes(src))
//            Toolkit.getDefaultToolkit().createImage(bytes)
//          }
//        }
//      }
//      val avatarIconsProvider = CachingIconsProvider(AsyncImageIconsProvider(iconsScope, AvatarLoader(requestExecutor)))
//      val reactionImagesLoader = GCReactionImageLoader(cs, account.server, requestExecutor)
//      val reactionIconsProvider = CachingIconsProvider(AsyncImageIconsProvider(iconsScope, reactionImagesLoader))
//
//      // repository might have been renamed/moved
//      val apiRepositoryPath = repositoryInfo.path
//      val apiRepositoryCoordinates = GCRepositoryCoordinates(account.server, apiRepositoryPath)
//
//      val repoDataService = GCPRRepositoryDataServiceImpl(ProgressManager.getInstance(), requestExecutor,
//                                                          remoteCoordinates, apiRepositoryCoordinates,
//                                                          repositoryInfo.owner,
//                                                          repositoryInfo.id, repositoryInfo.defaultBranch, repositoryInfo.isFork)
//      val securityService = GCPRSecurityServiceImpl(GitCodeSharedProjectSettings.getInstance(project),
//                                                    ghostUserDetails,
//                                                    account, currentUser,
//                                                    repositoryInfo)
//      val detailsService = GCPRDetailsServiceImpl(ProgressManager.getInstance(), requestExecutor, apiRepositoryCoordinates)
//      val stateService = GCPRStateServiceImpl(ProgressManager.getInstance(), project, securityService,
//                                              requestExecutor, account.server, apiRepositoryPath)
//      val commentService = GCPRCommentServiceImpl(ProgressManager.getInstance(), requestExecutor, apiRepositoryCoordinates)
//      val changesService = GCPRChangesServiceImpl(ProgressManager.getInstance(), project, requestExecutor,
//                                                  remoteCoordinates, apiRepositoryCoordinates)
//      val reviewService = GCPRReviewServiceImpl(ProgressManager.getInstance(), securityService, requestExecutor, apiRepositoryCoordinates)
//      val filesService = GCPRFilesServiceImpl(ProgressManager.getInstance(), requestExecutor, apiRepositoryCoordinates)
//      val reactionsService = GCReactionsServiceImpl(requestExecutor, apiRepositoryCoordinates)
//
//      val listLoader = GCPRListLoader(ProgressManager.getInstance(), requestExecutor, apiRepositoryCoordinates)
//      val listUpdatesChecker = GCPRListETagUpdateChecker(ProgressManager.getInstance(), requestExecutor, account.server, apiRepositoryPath)
//
//      val dataProviderRepository = GCPRDataProviderRepositoryImpl(project,
//                                                                  cs,
//                                                                  repoDataService,
//                                                                  detailsService,
//                                                                  stateService,
//                                                                  reviewService,
//                                                                  filesService,
//                                                                  commentService,
//                                                                  changesService) { id ->
//        GCGQLPagedListLoader(ProgressManager.getInstance(),
//                             SimpleGCGQLPagesLoader(requestExecutor, { p ->
//                               GCGQLRequests.PullRequest.Timeline.items(account.server, apiRepositoryPath.owner,
//                                                                        apiRepositoryPath.repository,
//                                                                        id.number, p)
//                             }, true))
//      }
//
//      val filesManager = GCPRFilesManagerImpl(project, apiRepositoryCoordinates)
//      val interactionState = project.service<GCPRPersistentInteractionState>()
//
//      val creationService = GCPRCreationServiceImpl(ProgressManager.getInstance(), requestExecutor, repoDataService)
//      ensureActive()
//      GCPRDataContext(cs, listLoader, listUpdatesChecker, dataProviderRepository,
//                      securityService, repoDataService, creationService, detailsService, reactionsService,
//                      imageLoader, avatarIconsProvider, reactionIconsProvider,
//                      filesManager, interactionState,
//                      GCPRDiffRequestModelImpl())
//    }
//  }
//
//  private class AvatarLoader(private val requestExecutor: GitCodeApiRequestExecutor)
//    : AsyncImageIconsProvider.AsyncImageLoader<String> {
//
//    private val avatarsLoader = CachingGCUserAvatarLoader.getInstance()
//
//    override suspend fun load(key: String): Image? =
//      avatarsLoader.requestAvatar(requestExecutor, key).asDeferred().await()
//
//    override fun createBaseIcon(key: String?, iconSize: Int): Icon =
//      IconUtil.resizeSquared(CollaborationToolsIcons.Review.DefaultAvatar, iconSize)
//
//    override suspend fun postProcess(image: Image): Image =
//      ImageUtil.createCircleImage(ImageUtil.toBufferedImage(image))
//  }
//
//  // dangerous to do this without lock, but making it suspendable is too much work
//  fun findContext(repositoryCoordinates: GCRepositoryCoordinates): GCPRDataContext? = cache[repositoryCoordinates]
//
//  companion object {
//    private val LOG = logger<GCPRDataContextRepository>()
//
//    @OptIn(ExperimentalCoroutinesApi::class)
//    private val IMAGES_DISPATCHER: CoroutineDispatcher = Dispatchers.IO.limitedParallelism(4)
//
//    fun getInstance(project: Project) = project.service<GCPRDataContextRepository>()
//  }
//}
